const { assert, expect } = require("chai")
const { network, deployments, ethers, getNamedAccounts } = require("hardhat")
const { devlopmentChains } = require("../../helper-hardhat-config")
require("@nomicfoundation/hardhat-chai-matchers")

!devlopmentChains.includes(network.name)
    ? describe.skip
    : describe("NFTSHOP contract test", () => {
          let deployer, NFTSHOP, BasicNFT, player, player2
          const price = ethers.utils.parseEther("0.1")
          const tokenid = 0
          beforeEach(async () => {
              deployer = (await getNamedAccounts()).deployer
              const accounts = await ethers.getSigners()
              player = accounts[1]
              player2 = accounts[2]

              await deployments.fixture(["all"])

              NFTSHOP = await ethers.getContract("NFTSHOP")
              BasicNFT = await ethers.getContract("BasicNFT")

              //NFTSHOP获取NFT合约的授权,调用mint方法一次tokenid为0.
              //执行完下面两条语句后tokenid为0的NFT被授权给NFTSHOP,且ownerAddress为deployer(account[0])
              await BasicNFT.mintNft()
              await BasicNFT.approve(NFTSHOP.address, tokenid)
          })
          describe("listItem", () => {
              it("emit when someone sell NFT", async () => {
                  //deployer address being seller
                  await expect(
                      NFTSHOP.listItem(BasicNFT.address, tokenid, price)
                  ).to.emit(NFTSHOP, "SaveDataToitemList")
              })
              it("when that NFT alreay exist revert", async () => {
                  //deployer address being seller
                  await expect(
                      NFTSHOP.listItem(BasicNFT.address, tokenid, price)
                  ).to.emit(NFTSHOP, "SaveDataToitemList")

                  await expect(
                      NFTSHOP.listItem(BasicNFT.address, tokenid, price)
                  ).to.revertedWithCustomError(
                      NFTSHOP,
                      "NFTSHOP__AlreadyHaveNFTData"
                  )
              })
              it("when owner dones't match address", async () => {
                  const player2connected = await NFTSHOP.connect(player2)
                  await expect(
                      player2connected.listItem(
                          BasicNFT.address,
                          tokenid,
                          price
                      )
                  ).to.revertedWithCustomError(NFTSHOP, "NFTSHOP__NotOwner")
              })
              it("update List when NFT selled", async () => {
                  await NFTSHOP.listItem(BasicNFT.address, tokenid, price)

                  console.log(
                      (
                          await NFTSHOP.getMainNftData(BasicNFT.address, 0)
                      ).toString()
                  )
                  assert(
                      (
                          await NFTSHOP.getMainNftData(BasicNFT.address, 0)
                      ).toString() ==
                          `${ethers.utils.parseEther("0.1")},${deployer}`
                  )
                  const playerconnected = await NFTSHOP.connect(player)
                  await playerconnected.BuyNft(BasicNFT.address, tokenid, {
                      value: price,
                  })
                  console.log(
                      (
                          await NFTSHOP.getMainNftData(BasicNFT.address, 0)
                      ).toString()
                  )
              })
              it("sell NFT need to be approve", async () => {
                  await BasicNFT.mintNft()
                  await expect(
                      NFTSHOP.listItem(BasicNFT.address, 1, price)
                  ).to.be.revertedWithCustomError(
                      NFTSHOP,
                      "NFTSHOP__NotApproverdForShop"
                  )
              })
          })

          describe("cancelSell", () => {
              it("revert when nft not in the shop", async () => {
                  await expect(
                      NFTSHOP.cancelSell(BasicNFT.address, 10)
                  ).to.be.revertedWithCustomError(
                      NFTSHOP,
                      "NFTSHOP__NftDataNotExist"
                  )
              })
              it("revert when someone call func but not NFT owner", async () => {
                  await NFTSHOP.listItem(BasicNFT.address, tokenid, price)
                  const player2connected = await NFTSHOP.connect(player2)
                  await expect(
                      player2connected.cancelSell(BasicNFT.address, tokenid)
                  ).to.be.revertedWithCustomError(NFTSHOP, "NFTSHOP__NotOwner")
              })
              it("cancel emit and del nftData", async () => {
                  await NFTSHOP.listItem(BasicNFT.address, tokenid, price)
                  await expect(
                      NFTSHOP.cancelSell(BasicNFT.address, tokenid)
                  ).to.emit(NFTSHOP, "NftCancelSell")
                  console.log(
                      (
                          await NFTSHOP.getMainNftData(BasicNFT.address, 0)
                      ).toString()
                  )
              })
              describe("BuyNft", () => {
                  it("revert when nftData donot exist", async () => {
                      const player2connected = await NFTSHOP.connect(player2)
                      expect(
                          player2connected.BuyNft(BasicNFT.address, 1)
                      ).to.be.revertedWithCustomError(
                          NFTSHOP,
                          "NFTSHOP__NftDataNotExist"
                      )
                  })
                  it("revert when price not enough ", async () => {
                      await NFTSHOP.listItem(BasicNFT.address, tokenid, price)

                      const player2connected = await NFTSHOP.connect(player2)
                      await expect(
                          player2connected.BuyNft(BasicNFT.address, tokenid, {
                              value: ethers.utils.parseEther("0.04"),
                          })
                      ).to.be.revertedWithCustomError(
                          NFTSHOP,
                          "NFTSHOP__NotEnoughToken"
                      )
                  })
              })

              describe("updateNftState", () => {
                  it("only NFT owner can update", async () => {
                      await NFTSHOP.listItem(BasicNFT.address, tokenid, price)
                      const player2connected = await NFTSHOP.connect(player2)
                      await expect(
                          player2connected.updateNftState(
                              BasicNFT.address,
                              tokenid,
                              ethers.utils.parseEther("1")
                          )
                      ).to.be.revertedWithCustomError(
                          NFTSHOP,
                          "NFTSHOP__NotOwner"
                      )
                  })
                  it("update to emit", async () => {
                      await NFTSHOP.listItem(BasicNFT.address, tokenid, price)
                      await expect(
                          NFTSHOP.updateNftState(
                              BasicNFT.address,
                              tokenid,
                              ethers.utils.parseEther("1")
                          )
                      ).to.emit(NFTSHOP, "SaveDataToitemList")
                  })
              })
              describe("withdrawProceeds", () => {
                  it("can't be withdraw with 0 proceed", async () => {
                      await expect(
                          NFTSHOP.withdrawProceeds()
                      ).to.be.revertedWithCustomError(
                          NFTSHOP,
                          "NFTSHOP__NoProceedsToWithdraw"
                      )
                  })
                  it("withdraw proceeds", async () => {
                      //deployer address being seller
                      await NFTSHOP.listItem(BasicNFT.address, tokenid, price)

                      //palayer Account buyer
                      const playerconnected = await NFTSHOP.connect(player)
                      await playerconnected.BuyNft(BasicNFT.address, tokenid, {
                          value: price,
                      })

                      assert(
                          (await BasicNFT.ownerOf(tokenid)).toString() ==
                              player.address
                      )
                      assert(
                          (
                              await NFTSHOP.getSellerProceeds(deployer)
                          ).toString() == price.toString()
                      )

                      await NFTSHOP.withdrawProceeds()
                      assert(
                          (
                              await NFTSHOP.getSellerProceeds(deployer)
                          ).toString() == 0
                      )
                  })
              })
          })
      })
